#include <string.h>

#include "projdefs.h"
#include "list.h"
#include "queue.h"
#include "task.h"

/* Constrants used with the cRxLock and cTxLock structure members. */
#define queueUNLOCKED             ( ( int8_t ) -1 )

#if 0
/**
 * @brief Sends an item to a queue.
 *
 * @param xQueue Handle to the queue to which the item will be sent.
 * @param pvItemToQueue Pointer to the item to be sent.
 * @param xTicksToWait  The maximum amount of time the task should block if the queue is full.
 * @param xCopyPosition Specifies where the item will be copied within the queue, values are:
 *                      - queueSEND_TO_BACK: Insert the item at the end of the queue.
 * @note  This function should not be called from an interrupt service routine (ISR).
 * @return 
 *        - pdPASS : 
 */
BaseType_t xQueueGenericSend()
{
	
}

#endif

BaseType_t xQueueGenericReset( QueueHandle_t xQueue, 
                               BaseType_t xNewQueue);

static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, 
	                                 const UBaseType_t uxItemSize,
																	 uint8_t * pucQueueStorate,
																	 QueueType_t queueType,
																	 Queue_t * pxNewQueue )
{
	pxNewQueue->pcHead = (int8_t *)pxNewQueue;
	
	// Initialise the queue members as described where the queue type is defined
	pxNewQueue->uxLength   = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	
	xQueueGenericReset( pxNewQueue, pdTRUE);
}


#if ( configSUPPORT_STATIC_ALLOCATION == 1)

/**
 * @brief Create a queue statically.
 * 
 * This function create a queue with the specified length and item size using statically allocated memory 
 * provided by the user. The created queue is of the type specified by 'ucQueueType'
 *
 * @param   uxQueueLength    The maximum number of items that the queue can hold.
 * @param   uxItemSize       The size of eath item in the queue, in bytes.
 * @param   pucQueueStorage  Pointer to the statically allocated memory to be used as the queue's storage area.
 * @param   queueType        The type of the queue to be created. It should be one of the enumerated types, see @QueueType_t
 * @note    This function should not be called from an interrupt service routine (ISR).
 * 
 * @return  A handle to the created queue, or NULL if the queue could not be created.
 * 
 * @note    This function is intended for internal use only and should not be called directly by application code.
 */
QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
                                         const UBaseType_t uxItemSize,
                                         uint8_t * pucQueueStorate,
																				 Queue_t *pxStaticQueue,
																				 const QueueType_t queueType)
{
	Queue_t * pxNewQueue = NULL;
	
	if(pxStaticQueue == NULL || uxQueueLength == 0 || uxItemSize == 0)
	{
		return pxNewQueue;
	}
	
	prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorate, queueType, pxNewQueue );
	
  return pxNewQueue;
}

#endif /* configSUPPORT_STATIC_ALLOCATION */

/**
 * @brief Resets a queue to its initial state.
 * 
 * @param xQueue    Handle to the queue to be reset.
 * @param xNewQueue Flag indicating whether to create a new queue in place of the reset queue.
 *                   - pdTRUE:  Create a new queue.
 *                   - pdFALSE: Do not create a new queue.
 * 
 * @return pdPASS if the queue was successfully reset, otherwise pdFAIL.
 *
 * @note This function should be use with caution, as it may lead to loss of data if queue is not empty.
 */
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, 
                               BaseType_t xNewQueue)
{
	if( xQueue == NULL || xQueue->uxLength < 1U || ( ( SIZE_MAX ) / xQueue->uxLength ) < xQueue->uxItemSize )
	{
		return pdFAIL;
	}
	
	// Disable scheduling and interrupts to ensure the atomicity of code within critical sections.
	taskENTER_CRITICAL();
	{
		xQueue->pointers.pcTail   = xQueue->pcHead + ( xQueue->uxLength * xQueue->uxItemSize );
		xQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;

		xQueue->pcWriteTo = xQueue->pcHead;
		xQueue->pointers.pcReadFrom = xQueue->pcHead + ( ( xQueue->uxLength - 1U ) * xQueue->uxItemSize );
		
		xQueue->cRxLock = queueUNLOCKED;
		xQueue->cTxLock = queueUNLOCKED;
	
		// Initialise the waiting task list
		vListInitialise( &( xQueue->xTaskWaitingToSend ) );
		vListInitialise( &( xQueue->xTaskWaitingToReceive ) );
	}
	taskEXIT_CRITICAL();
	return pdTRUE;
}

/**
 * @brief Add data to queue.
 * 
 * @param xQueue    The pointer to the queue.
 * @param xNewQueue The item that will add to the queue.
 * @param xPosition The place where item add to the queue.
 * 
 * @return pdPASS only
 *
 * @note This function should be called from a critical section.
 */
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue,  
	                                    const void * pvItemToQueue,
																			const BaseType_t xPosition )
{
	if( xPosition == queueSEND_TO_BACK )
	{
		( void ) memcpy( (void *) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
		pxQueue->pcWriteTo += pxQueue->uxItemSize;
	}
	
	if( pxQueue->pcWriteTo == pxQueue->pointers.pcTail)
	{
		// pcHead is a fixed pointer pointing to the head of the queue's memory, and it does not change.
		pxQueue->pcWriteTo = pxQueue->pcHead;
	}
	
	return pdPASS;
}